/* 
 * File:   AnomalyInjector.cc
 * Author: Lu
 * 
 * Created on 26 aprile 2010, 14.45
 */

#include "AnomalyInjector.h"
#include "composite.h"
#include "or-composite.h"
#include "string-leaf.h"
#include "actions/action.h"
#include <iterator>
#include <vector>
#include <fstream>
#include "actions/send-action.h"
#include "command-composite.h"

#include "injector-performer-factory.h"

AnomalyInjector::AnomalyInjector() {
    active = false;
}

AnomalyInjector::AnomalyInjector(const AnomalyInjector& orig) {
    active = false;
}

AnomalyInjector::~AnomalyInjector() {
    delete injector;
}

/**
 * Performs the anomaly injection based on the configuration.
 * This method visit the three and passes each leaf to an injector-performer that
 * returns a composite ptr to be attached to the parent node of the leaf substituting
 * the leaf itself.
 *
 */
bool AnomalyInjector::inject(Composite* root) {

    std::vector<Composite**>::iterator sons_iterator;
	std::vector<Composite**> datasons = root->getDataSons();
    for (sons_iterator = datasons.begin(); 
			sons_iterator != datasons.end(); 
			sons_iterator++) {

        if ((*(*sons_iterator))->isLeaf()) {
            Composite* branch = injector->inject(*(*sons_iterator));
            delete *(*sons_iterator);
            *(*sons_iterator) = branch;
        } else
            inject(*(*sons_iterator));
    }

}

Composite* AnomalyInjector::findSendComposite(Composite * root, std::vector<Composite*>& send_actions){
    std::vector<Composite*>::iterator sons_iterator;
	 std::vector<Composite*> sons = root->getSons();
    for (sons_iterator = sons.begin(); 
			sons_iterator != sons.end(); 
			sons_iterator++) {
        if(dynamic_cast<SendAction*>((*sons_iterator)->getAction()))
            send_actions.push_back( (*sons_iterator) );
        else
            findSendComposite((*sons_iterator), send_actions);
    }
}

bool AnomalyInjector::write(Composite* root) {

    if (outFileName.compare("") != 0) {
        std::ostringstream ooss(std::ostringstream::out);
        ooss << "; ABNF File Auto-Generated by S.T.R.E.S.S. Automatic Injector." << std::endl;
        ooss << "; \t The injection points are named \"or{id}\", while the anomalies \"anomaly_{id}_{nodeName}\"" << std::endl;
        ooss << std::endl;

        writeInjectedTreeToABNF(root, ooss);
        ooss << std::endl;

        std::ofstream file(outFileName.c_str());
        file << ooss.str();
        file.close();
        return true;
    } else
        return false;
}

/**
 * It should be called to configure the injection of anomalies and the type of output.
 * If fname is NULL the injection is performed only on the abnf tree whose root will
 * be passed to the method inject(). If fname is different from NULL and a file with
 * that name exists, an ANBF version of the injected tree will be written on that file.
 *
 */
bool AnomalyInjector::configure(bool strL, std::string fname, bool hexL, bool decL, bool binL) {

    outFileName = fname;
    //injector = new InjectorPerformer(strL, hexL, decL, binL);
 	injector = InjectorPerformerFactory::getInstance()->getPerformer();
}

void AnomalyInjector::configure(int p) {
    std::cout<<"USING: "<<p<<std::endl;
    switch (p) {
        case 0:
        {
            configure(false, "/tmp/self.bnf", false, false, false);
            break;
        }
        case 1:
        {
            configure(true, "/tmp/self.bnf", false, false, false);
            active = true;
            break;
        }
        case 2:
        {
            configure(true, "/tmp/self.bnf", true, false, false);
            active = true;
            break;
        }
        case 3:
        {
            configure(true, "/tmp/self.bnf", true, true, false);
            active = true;
            break;
        }
        case 4:
        {
            configure(true, "/tmp/self.bnf", true, true, true);
            active = true;
            break;
        }
        default:
        {
            configure(false, "/tmp/self.bnf", false, false, false);
            break;
        }

    }
}

bool AnomalyInjector::writeInjectedTreeToABNF(Composite* t, std::ostringstream& ooss) {
	std::vector<Composite*>::iterator iter;
    if (t->getSons().size() != 0){
        ooss << t->getName()<<"-"<<t->getId() << " =";
	}
    for (iter = t->getSons().begin(); iter != t->getSons().end(); iter++) {
        if (dynamic_cast<OrComposite*> (t) && iter != t->getSons().begin())
            ooss << " / ";
        else
            ooss << " ";
        if (dynamic_cast<StringLeaf*> (*iter))
            ooss << getNodeName4File((*iter));
		else if(dynamic_cast<CommandComposite*> (*iter) || dynamic_cast<Leaf*> (*iter))
			ooss << (*iter)->getName();
        else
            ooss << (*iter)->getName()<<"-"<<(*iter)->getId();
    }
    ooss << std::endl;
    for (iter = t->getSons().begin(); iter != t->getSons().end(); iter++) {
        writeInjectedTreeToABNF(*iter, ooss);
    }
}

std::string AnomalyInjector::getNodeName4File(Composite* orig) {
    std::string ret = trim(orig);

    return addQuote(ret);
}

std::string AnomalyInjector::trim(Composite* orig) {
    std::string origName = orig->getName();
    origName = origName.erase(0, (origName.find_first_of("\"") + 1));
    origName = origName.erase(origName.find_last_of("\\"));

    return origName;
}

std::string AnomalyInjector::addQuote(std::string s) {
    std::ostringstream ooss(std::ostringstream::out);
    ooss << "\"" << s << "\"";
    return ooss.str();
}

